﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.Common.Data;
using ScrumTable.Common.Data.Tracking;
using ScrumTable.Common.Net;
using ScrumTable.DL.Data.Schema;
using ScrumTable.DL.Data.Schema.ScrumTableV1;

#endregion

namespace ScrumTable.DL.Data.Generic.Cache
{
    /// <summary>
    /// This class contains all cached data elements.  All data
    /// mutations of should be executed on the current class.
    /// </summary>
    public class CacheElementFactory
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private const string TypePrefix = "Cache";

        private readonly IdGenerator _idGenerator;
        private readonly Func<string, int, string> _uniqueIdGetAction;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the underlying IdGenerator instance which is used to create
        /// the new unique id's of a CacheElement.
        /// </summary>
        public IdGenerator IdGenerator
        {
            get { return _idGenerator; }
        }

        /// <summary>
        /// Gets a function pointer which is used to retrieve a unique id
        /// from a type and backend id.
        /// </summary>
        protected virtual Func<string, int, string> UniqueIdGetAction
        {
            get { return _uniqueIdGetAction; }
        }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Initializes a new instance of the <see cref="CacheElementFactory"/> class.
        /// This constructor consumes an action and a generator which are used
        /// during the creation 
        /// </summary>
        internal CacheElementFactory(IdGenerator generator, Func<string, int, string> uniqueIdGetAction = null)
        {
            PreCondition.AssertNotNull(generator, "generator");

            _idGenerator = generator;
            _uniqueIdGetAction = uniqueIdGetAction;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CacheElementFactory"/> class.
        /// </summary>
        internal CacheElementFactory()
        {
            _idGenerator = new IdGenerator();
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Retrieves the Factory category of the given type.
        /// </summary>
        /// <returns>Returns the factory category.</returns>
        public static string GetCategoryByType(Type toGetCategory)
        {
            PreCondition.AssertNotNull(toGetCategory, "toGetCategory");

            return toGetCategory.Name.StartsWith(TypePrefix)
                       ? toGetCategory.Name.Substring(TypePrefix.Length)
                       : toGetCategory.Name;
        }

        /// <summary>
        /// Creates a new CacheIteration instance.
        /// </summary>
        public CacheIteration CreateIteration(
            string backEndId,
            int id,
            double orderIndex,
            string name,
            string iterationPath,
            DateTime startDate,
            DateTime endDate)
        {
            return new CacheIteration(GetUniqueId<CacheIteration>(id), backEndId, id,
                orderIndex, name, iterationPath,
                startDate, endDate );
        }

        /// <summary>
        /// Creates a new CacheFieldDefinition instance. This method creates
        /// a field definition which has a back end field definition and an identifier.
        /// </summary>
        public CacheFieldDefinition CreateFieldDefinition(
            int id,
            string displayName,
            SchemaBase schema,
            DataType type,
            IEnumerable<object> allowedValues,
            object defaultValue = null)
        {
            return new CacheFieldDefinition(
                GetUniqueId<CacheFieldDefinition>(id), id, displayName,
                schema, type, allowedValues,
                defaultValue);
        }

        /// <summary>
        /// Creates a new CacheFieldDefinition instance. This method is used in
        /// the current library only and creates a field definition which has
        /// a back end field definition.
        /// </summary>
        public CacheFieldDefinition CreateFieldDefinition(
            string displayName,
            SchemaBase schema,
            DataType type,
            IEnumerable<object> allowedValues,
            object defaultValue = null)
        {
            return new CacheFieldDefinition(
                GetUniqueId<CacheFieldDefinition>(), CacheIdentity.DefaultId, displayName,
                schema, type, allowedValues,
                defaultValue);
        }

        /// <summary>
        /// Creates a new CacheFieldDefinition instance. This method creates a
        /// virtual field definitions (for example for the reporting fields).
        /// </summary>
        public CacheFieldDefinition<T> CreateFieldDefinition<T>(
            string name,
            string displayName,
            DataType dataFormat, 
            T defaultValue,
            IEnumerable<T> allowedValues = null,
            IEnumerable<T> suggestedValues = null)
            where T : class
        {
            return new CacheFieldDefinition<T>(
                GetUniqueId<CacheFieldDefinition>(), name, displayName,
                dataFormat, defaultValue, allowedValues,
                suggestedValues);
        }

        /// <summary>
        /// Creates a new CacheFieldDefinition instance. This method creates
        /// a virtual field definition which field value must be stored into the settings.
        /// </summary>
        public CacheFieldDefinition CreateFieldDefinition(SchemaBase schema)
        {
            return new CacheFieldDefinition(GetUniqueId<CacheFieldDefinition>(), schema);
        }

        /// <summary>
        /// Creates a new CreateReport instance.
        /// </summary>
        public CacheReport CreateReport(
            string name,
            string path,
            Uri fullPath,
            string postCommandArguments,
            IEnumerable<CacheFieldDefinition<NamedValue>> fieldDefinitions)
        {
            return new CacheReport(
                GetUniqueId<CacheReport>(), name, path,
                fullPath, postCommandArguments, fieldDefinitions);
        }

        /// <summary>
        /// Creates a new CacheMember instance.
        /// </summary>
        public CacheMember CreateMember(
            double orderIndex,
            string displayName,
            string mailAddress,
            string accountName,
            string domain,
            string backEndId,
            bool hasLogInData,
            IEnumerable<string> teams)
        {
            return new CacheMember(
                GetUniqueId<CacheMember>(), orderIndex, displayName,
                mailAddress, accountName, domain,
                backEndId, hasLogInData, teams);
        }

        /// <summary>
        /// Creates a new CacheTeam instance.
        /// </summary>
        public CacheTeam CreateTeam(
            double orderIndex,
            string displayName,
            string backEndId)
        {
            return new CacheTeam(
                GetUniqueId<CacheTeam>(), orderIndex, displayName,
                backEndId);
        }

        /// <summary>
        /// Creates a new CacheProject instance.
        /// </summary>
        public CacheProject CreateProject(
            int id,
            string name,
            ServiceRegistration projectPortal,
            ServiceRegistration reportingPortal,
            IEnumerable<CacheTeam> teams,
            IEnumerable<CacheMember> members,
            IEnumerable<CacheWorkItemType> workItemTypes)
        {
            return new CacheProject(
                GetUniqueId<CacheProject>(id), id, name,
                projectPortal, reportingPortal, teams,
                members, workItemTypes);
        }

        /// <summary>
        /// Creates a new CacheWorkItemLink instance.
        /// </summary>
        /// <param name="relatedId">Specifies the related work item tfs id.</param>
        public CacheWorkItemLink CreateWorkItemLink(int relatedId)
        {
            return new CacheWorkItemLink(GetUniqueId<CacheWorkItemLink>(), relatedId);
        }

        /// <summary>
        /// Creates a new CacheWorkItemLink instance.
        /// </summary>
        /// <param name="relatedId">Specifies the related work item tfs id.</param>
        /// <param name="relatedUniqueId">Specifies the related unqiue CacheWorkItem id.</param>
        public CacheWorkItemLink CreateWorkItemLink(int relatedId, string relatedUniqueId)
        {
            return new CacheWorkItemLink(GetUniqueId<CacheWorkItemLink>(), relatedId, relatedUniqueId);
        }

        /// <summary>
        /// Creates a new CacheWorkItemType instance.
        /// </summary>
        public CacheWorkItemType CreateWorkItemType(
            string typeName,
            SchemaBase schema,
            IEnumerable<CacheFieldDefinition> fieldDefinitions,
            CacheWorkflow workflow)
        {
            return new CacheWorkItemType(
                GetUniqueId<CacheWorkItemType>(), typeName,
                schema, CacheWorkItemType.LoadFieldDefinitionsFromSchema(this, schema, fieldDefinitions), workflow);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CacheWorkItem"/> class.
        /// </summary>
        /// <param name="type">Specifies the work item type.</param>
        /// <param name="id">Specifies the team foundation id.</param>
        /// <param name="name">Specfies the work item title.</param>
        /// <param name="revision">Specifies the current revision of the work item.</param>
        /// <param name="iterationId">Specifeis the parent iteration id.</param>
        /// <param name="state">Specifies the workflow state of the work item.</param>
        /// <param name="description">Specifies the work item description.</param>
        /// <param name="assignedTo">Specifies the display name of the assigned member. Provide an empty string if no member is assigned.</param>
        /// <param name="values">Specifies a set of values corresponding to the underlying Schema to load.</param>
        /// <param name="parentLinks">Specifies the parent links.</param>
        /// <param name="childLinks">Specifies the child links.</param>
        /// <param name="attachments">Specifies a list of attachments stored behind the current work item.</param>
        /// <param name="revisions">Specifies a list of revisions stored behind the current work item.</param>
        public CacheWorkItem CreateWorkItem(
            CacheWorkItemType type,
            int id,
            string name,
            int revision,
            int iterationId,
            string state,
            string description,
            string assignedTo,
            IDictionary<string, object> values,
            IEnumerable<CacheWorkItemLink> parentLinks,
            IEnumerable<CacheWorkItemLink> childLinks,
            IEnumerable<CacheAttachment> attachments,
            IEnumerable<CacheWorkItemRevision> revisions)
        {
            return new CacheWorkItem(
                GetUniqueId<CacheWorkItem>(id), type, id,
                name, revision, iterationId,
                state, description, assignedTo,
                values, parentLinks, childLinks,
                attachments, revisions);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CacheWorkItem"/> class.
        /// </summary>
        /// <param name="type">Specifies the work item type.</param>
        public CacheWorkItem CreateWorkItem(CacheWorkItemType type)
        {
            return new CacheWorkItem(GetUniqueId<CacheWorkItem>(), type);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CacheWorkflow"/> class.
        /// </summary>
        /// <param name="backEndTransitions">Specifies a list of transitions which should be filled into the new workflow.</param>
        /// <param name="stateSchema">Specifies the schema definition of the underlying State field.</param>
        /// <param name="isStrictWorkflow">Specifies a boolean which indicates that the workflow is customized and does not follow special back-end system rules.</param>
        /// <param name="backEndStates">Specifies the states of the back-end system. This argument is used to validate the workflow transition override through the schema.</param>
        public CacheWorkflow CreateWorkflow(
            IEnumerable<CacheTransition> backEndTransitions,
            SchemaBase stateSchema,
            bool isStrictWorkflow,
            IEnumerable<string> backEndStates = null)
        {
            return new CacheWorkflow(
                GetUniqueId<CacheWorkflow>(),
                backEndTransitions,
                CacheTransition.LoadTransitionsFromSchema(this, stateSchema),
                stateSchema,
                isStrictWorkflow,
                backEndStates);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CacheTransition"/> class.
        /// </summary>
        /// <param name="transitionStateFrom">Specifies the transition FROM a state to another state.</param>
        /// <param name="transitionStateTo">Specifies the transition from a state TO another state.</param>
        public CacheTransition CreateTransition(string transitionStateFrom, string transitionStateTo)
        {
            return new CacheTransition(GetUniqueId<CacheTransition>(), transitionStateFrom, transitionStateTo);
        }

        /// <summary>
        /// Initializes all transitions between the given allowed enumerable.
        /// </summary>
        /// <param name="allowedTransitions">Specifies all allowed transitions between the states.</param>
        public IEnumerable<CacheTransition> CreateTransitions(IEnumerable<string> allowedTransitions)
        {
            return CacheTransition.LoadTransitionsFromValues(this, allowedTransitions);
        }

        /// <summary>
        /// Initializes all transitions between the given allowed enumerable.
        /// </summary>
        /// <param name="allowedTransitions">Specifies all allowed transitions between the states.</param>
        public IEnumerable<CacheTransition> CreateTransitions(IEnumerable<object> allowedTransitions)
        {
            return CreateTransitions(from transition in allowedTransitions select transition.ToString());
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CacheAttachment"/> class.
        /// </summary>
        /// <param name="name">Specifies the attachment name.</param>
        /// <param name="id">Specifies the back end system identifier.</param>
        /// <param name="description">Specifies the attachment description (comment) field.</param>
        /// <param name="uri">Specifies the uri of an attachment.</param>
        public CacheAttachment CreateAttachment(string name, int id, string description, Uri uri)
        {
            return new CacheAttachment(
                GetUniqueId<CacheAttachment>(id), id, name,
                description, uri);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CacheWorkItemRevision"/> class.
        /// </summary>
        /// <param name="id">Specifies the back end system identifier.</param>
        /// <param name="tagLine">Specifies the revision tag line.</param>
        /// <param name="history">Specifies the revision history text.</param>
        public CacheWorkItemRevision CreateRevision(int id, string tagLine, string history)
        {
            return new CacheWorkItemRevision(
                GetUniqueId<CacheWorkItemRevision>(id), id, tagLine,
                history);
        }

        /// <summary>
        /// Gets a new unique id (as string) for an element with the given id
        /// (as integer). The id as integer may be specified by a backend system.
        /// </summary>
        /// <param name="category">Specifies id of the category to create.</param>
        /// <param name="id">Specifies the back end id of the element to create. This parameter may be used to retrieve an already stored element.</param>
        /// <returns>Returns the generated / retrieved id.</returns>
        protected string GetUniqueId(string category, int id = CacheIdentity.DefaultId)
        {
            if (UniqueIdGetAction != null)
            {
                string uniqueId = UniqueIdGetAction(category, id);

                if (!string.IsNullOrEmpty(uniqueId))
                {
                    return uniqueId;
                }
            }
            return _idGenerator.NewId(category);
        }

        private string GetUniqueId<T>(int id = CacheIdentity.DefaultId)
        {
            return GetUniqueId(GetCategoryByType(typeof(T)), id);
        }

        #endregion

        #region Events

        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}